#pragma once

#include <thread>
#include <atomic>
#include <memory>
#include "queue.h"


namespace evm { namespace common {

static constexpr int DEFAULT_MAX_QUEUE_SIZE = 1024;

/* Thread pool for dynamic queueing tasks and executing asynchronously.
 * Pool size can be increased for concurrency.
 * Queue size can be increased for throughput. */
struct ThreadPool {
    using Task = function<void (void*)>;

    /* Construct thread pool, with specified pool size and optionally queue size. */
    ThreadPool(size_t pool_size, size_t queue_max_size = DEFAULT_MAX_QUEUE_SIZE);

    /* Post a task into the thread pool, with optionally user data pointer `data`.
     * When queue is full:
     * if `block` is true, block until queue becomes not full;
     * if `block` is false, task is not posted and false is returned. */
    bool post(Task task, void* data = nullptr, bool block = false);

    /* Start threads for executing tasks in queue. */
    void start();

    /* Stop threads.
     * If `wait` is true, wait until all tasks have been gotten and processed. */
    void stop(bool wait = false);

    /* Wait for threads exiting. */
    void join();

    /* Checks if joinable. */
    bool joinable();

    /* Return the approximate count of unprocessed tasks. */
    size_t tasks();

private:
    /* Queue for storing tasks and user data pointers. */
    Queue<pair<Task, void*>> queue_;
    /* Thread pool. */
    vector<thread> threads_;
    /* Thread pool size. */
    size_t pool_size_;
    /* Whether the thread pool is in stopped state. */
    atomic<bool> stopped_{true};
};

}}
